#include "MazeMap.h"

MazeMap::MazeMap(Character* aHero, int aSeed, int aBiome) : Map(aHero, aSeed)
{
	fprintf(stderr, "> Maze constructor...");

	_mapType = MAP_MAZE;
	_biome = aBiome;

	Generate(31,31);

	fprintf(stderr, " - OK\n");
}

struct MazeNode
{
	bool added;
	bool rightEdge;
	bool bottomEdge;
};

void MazeMap::GenerateMapData()
{
	MazeNode mn = {false, false, false};
	Tab2D<MazeNode> Maze(_tilesX/2,_tilesY/2,mn);

	Node start = {rand()%Maze.getSizeW(),rand()%Maze.getSizeH()};
	Node actualNode = start;
	vector<Node> possibleWays;

	while(true)
	{
		// Kill
		while(true)
		{
			Maze(actualNode.x,actualNode.y).added = true;
			possibleWays.clear();
			for(int d=0; d<4; d++)
			{
				Node pos = {actualNode.x + dir[d][0], actualNode.y + dir[d][1]};
				if(Maze.IsLegal(pos.x,pos.y))
					if(!Maze(pos.x,pos.y).added)
						possibleWays.push_back(pos);
			}
			if(possibleWays.empty()) break;

			Node newNode = possibleWays[rand()%possibleWays.size()];
			if(newNode.x < actualNode.x) Maze(newNode.x,newNode.y).rightEdge = true;
			if(newNode.y < actualNode.y) Maze(newNode.x,newNode.y).bottomEdge = true;
			if(newNode.x > actualNode.x) Maze(actualNode.x,actualNode.y).rightEdge = true;
			if(newNode.y > actualNode.y) Maze(actualNode.x,actualNode.y).bottomEdge = true;

			actualNode = newNode;
		}

		// Hunt
		bool found = false;
		for(int x=0; x<Maze.getSizeW(); x++)
		for(int y=0; y<Maze.getSizeH(); y++)
		{
			if(!Maze(x,y).added)
			{
				for(int d=0; d<4; d++)
				{
					Node pos = {x + dir[d][0], y + dir[d][1]};
					if(Maze.IsLegal(pos.x,pos.y) && Maze(pos.x,pos.y).added)
					{
						actualNode = pos;
						found = true;
						goto contin;
					}
				}
			}
		}
		contin:
		if(!found) break;
	}

	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		setLower(x,y,F_STONE);
		setUpper(x,y,O_WALL);
		SetPassable(x,y,false);
	}

	for(int x=0; x<Maze.getSizeW(); x++)
	for(int y=0; y<Maze.getSizeH(); y++)
	{
		int rx = x*2+1;
		int ry = y*2+1;
		setUpper(rx,ry,OBJECT_NONE);
		SetPassable(rx,ry,true);
		if(Maze(rx/2,ry/2).rightEdge)
		{
			setUpper(rx+1,ry,OBJECT_NONE);
			SetPassable(rx+1,ry,true);
		}
		if(Maze(rx/2,ry/2).bottomEdge)
		{
			setUpper(rx,ry+1,OBJECT_NONE);
			SetPassable(rx,ry+1,true);
		}
	}

	// objects
	vector<Node> possibleWalls;
	for(int x=0; x<_tilesX; x+=2)
	for(int y=0; y<_tilesY; y+=2)
	{
		if(getUpper(x,y) == O_WALL && getUpper(x,y+1) == OBJECT_NONE)
		{
			Node wall = {x,y};
			possibleWalls.push_back(wall);
		}
	}

	// torches
	int numberOfTorches = _tilesX*_tilesY/15;
	for(int i=0; i<numberOfTorches; i++)
	{
		int randomWall = rand()%possibleWalls.size();
		Node n = possibleWalls[randomWall];
		setUpper(n.x,n.y,O_TORCH);
		possibleWalls.erase(possibleWalls.begin() + randomWall);

		int fakeMagicTileSize = 64;
		Node pos = {n.x*fakeMagicTileSize+fakeMagicTileSize/2,n.y*fakeMagicTileSize+fakeMagicTileSize*2/3};
		ParticleSystem* ps = new ParticleSystem(PARTICLE_SMOKE, pos);
		effects.push_back(ps);
		Point poi;
		poi.x = pos.x;
		poi.y = pos.y+20;
		LightSource ls(LIGHT_TORCH, poi);
		lightSpots.push_back(ls);
	}

	// ladders
	Node ladder = possibleWalls[rand()%possibleWalls.size()];
	setUpper(ladder.x,ladder.y,O_LADDER);
	SetPassable(ladder.x,ladder.y,true);
	ladder = possibleWalls[rand()%possibleWalls.size()];
	setUpper(ladder.x,ladder.y,O_LADDER);
	SetPassable(ladder.x,ladder.y,true);

	// treasures
	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		if(getUpper(x,y) != OBJECT_NONE) continue;

		int wallsCount = 0;
		for(int d=0; d<4; d++)
			if(getUpper(x+dir[d][0],y+dir[d][1]) == O_WALL)
				wallsCount++;

		static EObject objectTab[] = {O_BARREL, O_CHEST, O_CRATE};
		if(wallsCount == 3)
		{
			setUpper(x,y,objectTab[rand()%3]);
			SetPassable(x,y,false);
		}
	}
}

Node MazeMap::GetHeroSpawn(int aFacing)
{
	Node n;
	do
	{
		n.x = rand()%_tilesX;
		n.y = rand()%_tilesY;
	} while(!IsPassable(n.x,n.y) || IsPassable(n.x,n.y-1));

	setUpper(n.x,n.y-1,O_LADDER);
	SetPassable(n.x,n.y-1,true);

	return n;
}